<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
    </style>
</head>

<body>
    <canvas id="canvas"></canvas>
</body>
<script id="LightTextureVS" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    uniform mat4 u_PvM4;
    uniform mat4 u_ModelM4;
    void main(){
        gl_Position = u_PvM4 * u_ModelM4 * a_Position;
        gl_PointSize = 30.0;
    }
</script>
<script id="LightTextureFS" type="x-shader/x-fragment">
    precision mediump float;
    void main(){
        gl_FragColor = vec4(vec3(gl_FragCoord.z), 1.0);
    }
</script>
<script id="TextureVS" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    uniform mat4 u_PvM4;
    uniform mat4 u_ModelM4;
    uniform mat4 u_LightPvM4;//灯光的投影矩阵
    varying vec4 v_ClipposLight;//剪贴灯(计算投影)
    void main(){
        vec4 worldPosi = u_ModelM4 * a_Position;
        gl_Position = u_PvM4 * worldPosi;
        v_ClipposLight = u_LightPvM4 * worldPosi;
    }
</script>
<script id="TextureFS" type="x-shader/x-fragment">
    precision mediump float;
    varying vec4 v_ClipposLight;//剪贴灯(计算投影)
    uniform sampler2D u_ShadowMap;//阴影贴图
    bool isInShadow(){
        //将世界坐标转为裁剪坐标
        vec3 fragPos = (v_ClipposLight.xyz / v_ClipposLight.w) / 2.0 + 0.5;
        vec4 shadowFrag = texture2D(u_ShadowMap, fragPos.xy);
        return fragPos.z > shadowFrag.r + 1.0 / 256.0;//最后的+数是调精度
    }
    void main(){
        float shadow = isInShadow() ? 0.7 : 1.0;
        gl_FragColor = vec4(vec3(shadow), 1.0);
        //gl_FragColor = vec4(1.0,1.0,1.0,1.0);
    }
</script>
<script type="module">
    import { Matrix4, Vector3, Color, PerspectiveCamera, Spherical } from '../js/three.module.js';
    import { CreateProgram, imgPromise } from '../js/Utils.js';
    import lib from '../js/ThreeLib.js';
    import CameraController from '../js/CameraController.js'
    import Poly from '../js/Poly4.js'
    import Control from '../js/Control.js'

    import Geo from '../js/Poly2/Geo.js'
    import Mat from '../js/Poly2/Mat.js'
    import Obj3D from '../js/Poly2/Obj3D.js'
    import Scene from '../js/Poly2/Scene.js'



    //#region gl
    const canvas = document.getElementById("canvas");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight - 4;

    const gl = canvas.getContext("webgl", { antialias: true, });

    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);//去图片描边
    gl.enable(gl.DEPTH_TEST);
    //#endregion

    //灯光贴图的program
    const lightProgram = CreateProgram(gl,
        document.querySelector('#LightTextureVS').innerText,
        document.querySelector('#LightTextureFS').innerText);

    const program = CreateProgram(gl,
        document.querySelector('#TextureVS').innerText,
        document.querySelector('#TextureFS').innerText);

    //灯光
    const light = new PerspectiveCamera(60, canvas.width / canvas.height, 0.1, 10);
    light.position.set(0, 0.3, 0);
    light.lookAt(0, 0, 0.0);
    light.updateMatrixWorld(true);
    const lightPvM4 = light.projectionMatrix.clone().multiply(light.matrixWorldInverse);

    //摄像机
    const camera = new PerspectiveCamera(45, canvas.width / canvas.height, 0.1, 10);
    camera.position.set(1, 1, 1);
    camera.lookAt(0, 0, 0.0);
    camera.updateMatrixWorld(true);
    const cameraPvM4 = camera.projectionMatrix.clone().multiply(camera.matrixWorldInverse);

    //三角形对象
    const triangleH = 0.1;
    const triangle = [
        -0.1, triangleH, 0.1,
        0.1, triangleH, 0.1,
        0.0, triangleH, -0.1,
    ];

    const floorH = 0.0;
    //投影的平面对象
    const floor = [
        -0.2, 0.0, 0.2,
        0.2, 0.0, 0.2,
        -0.2, 0.0, -0.2,
        0.2, 0.0, -0.2,
    ];

    const [textureW, textureH] = [1024, 1024];

    //main
    {
        const textureIndex = 0;

        //获取阴影的纹理贴图
        const texture = drawTexture(lightProgram, textureIndex);

        //绘制图形
        drawObj3D(program, texture, textureIndex);
    }


    //绘制纹理对象
    function drawObj3D(program, texture, textureindex) {
        gl.clearColor(0, 0, 0, 1);
        gl.clear(gl.COLOR_BUFFER_BIT);

        gl.useProgram(program);

        const u_PvM4 = gl.getUniformLocation(program, 'u_PvM4');
        const u_ModelM4 = gl.getUniformLocation(program, 'u_ModelM4');
        const u_LightPvM4 = gl.getUniformLocation(program, 'u_LightPvM4');
        const u_ShadowMap = gl.getUniformLocation(program, 'u_ShadowMap');
        gl.uniformMatrix4fv(u_PvM4, false, cameraPvM4.elements);
        gl.uniformMatrix4fv(u_ModelM4, false, new Matrix4().elements);
        gl.uniformMatrix4fv(u_LightPvM4, false, lightPvM4.elements);
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.uniform1i(u_ShadowMap, textureindex);

        draw(program, triangle);
        draw(program, floor);
    }

    //绘制纹理贴图
    function drawTexture(program, textureIndex) {
        gl.clearColor(0, 0, 0, 1);
        gl.clear(gl.COLOR_BUFFER_BIT);

        //创建纹理贴图TextureBuffer
        const textburBuffer = _createTextureBuffer(textureIndex);
        //创建帧缓冲区RenderBuffer
        const frameBuffer = _createFrameBuffer(textburBuffer, textureIndex);
        //创建深度缓冲区depthBuffer
        const depthbuffer = _createDepthBuffer();

        //修改绘制大小
        gl.viewport(0, 0, textureW, textureH)
        //绘制
        gl.useProgram(program);
        const u_PvM4 = gl.getUniformLocation(program, 'u_PvM4');
        const u_ModelM4 = gl.getUniformLocation(program, 'u_ModelM4');
        gl.uniformMatrix4fv(u_PvM4, false, lightPvM4.elements);
        gl.uniformMatrix4fv(u_ModelM4, false, new Matrix4().elements);
        draw(program, triangle);
        draw(program, floor);

        //置空帧缓冲区(一定要置空, 不然下一次绘制还会绘制在帧缓冲区中)
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        //恢复画布的渲染尺寸
        gl.viewport(0, 0, canvas.width, canvas.height);

        return textburBuffer;
    }

    function _createTextureBuffer(index) {
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
        gl.activeTexture(gl[`TEXTURE${index}`]);
        const textureBuffer = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, textureBuffer);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);//设置滤波器
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureW, textureH, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
        return textureBuffer;
    }

    function _createFrameBuffer(textureBuffer, index) {
        const frameBuffer = gl.createFramebuffer();
        gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textureBuffer, index);
        return frameBuffer;
    }

    function _createDepthBuffer() {
        const depthbuffer = gl.createRenderbuffer();
        gl.bindRenderbuffer(gl.RENDERBUFFER, depthbuffer);
        gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, textureW, textureH);//配装深度缓冲区
        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthbuffer);//向缓冲区中添加渲染缓冲区
        return depthbuffer;
    }


    function draw(program, position) {
        gl.useProgram(program);
        const a_Position = gl.getAttribLocation(program, 'a_Position');
        const buffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(position), gl.STATIC_DRAW);
        gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(a_Position);

        gl.drawArrays(gl.TRIANGLE_STRIP, 0, position.length / 3);
    }


</script>

</html>